"use strict";

const fs = require('fs');
const os = require('os');
const path = require('path');
const crypto = require('crypto');
const process = require('process')
const { execSync, execFileSync, spawn, spawnSync } = require("child_process");

const yaml = require('yamljs');
const images = require('images');

let backData;
let configData;

// 初始化参数
function init(projectDir, toolsDir) {
    console.log(`工程目录=${projectDir}`, `工具目录=${toolsDir}`);
    const configPath = path.join(projectDir, 'settings', 'buildConfig_Jsb', 'pack.config.yml');
    const backPath = path.join(projectDir, 'settings', 'buildConfig_Jsb', 'pack.back.json');
    if (!fs.existsSync(configPath)) {
        console.error(`配置文件不存在`, configPath);
        return;
    }
    configData = fs.readFileSync(configPath).toString();
    configData = yaml.parse(configData);
    configData.projectDir = projectDir;
    configData.toolsDir = toolsDir;

    // 输出目录
    if (os.platform() === 'darwin') { // 苹果
        configData.outputDir = configData.macBuildPath;
    } else if (os.platform() === 'win32') { // windows
        configData.outputDir = configData.winBuildPath;
    } else {
        console.error('平台不支持');
        return;
    }
    console.log('配置内容', configData);
    mkdirSync(configData.outputDir);

    if (fs.existsSync(backPath)) {
        backData = fs.readFileSync(backPath);
        try {
            backData = JSON.parse(backData);
        } catch (e) { }
    }
    if (null == backData) {
        backData = {};
    }
    backData = Object.assign({ clientCode: 0, appCode: 0 }, backData);
    console.log('备份内容', backData);

    // 开始构建
    startBuild();
}
module.exports.init = init;

function startBuild() {
    let enginePath;
    if (os.platform() === 'darwin') {
        enginePath = `/Applications/CocosCreator/Creator/${configData.engineVer}/CocosCreator.app/Contents/MacOS/CocosCreator`;
    } else if (os.platform() === 'win32') {
        enginePath = `D:/CocosDashboard_1.0.18/resources/.editors/Creator/${configData.engineVer}/CocosCreator.exe`

    }
    let outputDir = path.join(configData.outputDir, configData.title);
    if (configData.clientCode > backData.clientCode) {
        // 构建Cocos 原生版本
        console.log('构建JSB版本开始');
        const appABIs = JSON.stringify(configData.appABIs).replace(/"/g, '\'');
        const cmd = `${enginePath} --path ${configData.projectDir} --build "title=${configData.title};platform=${configData.platform};buildPath=${outputDir};inlineSpriteFrames=${configData.inlineSpriteFrames};mergeStartScene=false;optimizeHotUpdate=${configData.optimizeHotUpdate};template=${configData.template};packageName=${configData.packageName};apiLevel=${configData.apiLevel};appABIs=${appABIs};useDebugKeystore=${configData.useDebugKeystore};orientation={'portrait':${configData.orientation == 'portrait'},'landscapeLeft':${configData.orientation != 'portrait'},'landscapeRight':${configData.orientation != 'portrait'}};debug=${configData.isDebug};encryptJs=${!configData.isDebug};zipCompressJs=true;md5Cache=${configData.md5Cache};sourceMaps=true"`;
        try {
            execSync(cmd);
        } catch (e) {
            console.log(`构建JSB版本错误：${e}`);
            return;
        }

        // 制作热更包
        let bool = uploadHotUpdate();
        if (!bool) {
            return;
        }

        backData.clientCode = configData.clientCode;
        saveConfig();
        console.log('构建JSB版本成功');
    }




    // 安卓打包
    if (configData.appCode > backData.appCode) {
        let bool = packAPK();
        if (!bool) {
            return;
        }
        backData.appCode = configData.appCode;
        saveConfig();
        console.log('安卓构建成功');
    }
}

function uploadHotUpdate() {
    console.log('开始制作热更包');
    const ver = configData.clientVer + "." + configData.clientCode;
    const buildDir = path.join(configData.outputDir, configData.title, 'jsb-link');
    const hotUpdateDir = path.join(configData.outputDir, configData.title, 'hotupdate', ver);
    copyFileSync(path.join(buildDir, 'src'), path.join(hotUpdateDir, 'src'));
    // 2.4.0及以上引擎
    if (compareVersion(configData.engineVer, '2.4.0')) {
        // 移动assets
        copyFileSync(path.join(buildDir, 'assets'), path.join(hotUpdateDir, 'assets'));
    } else {
        // 移动res和subpackages
        // copyFileSync(path.join(buildDir, 'res'), path.join(hotUpdateDir, 'res'));
        // copyFileSync(path.join(buildDir, 'subpackages'), path.join(hotUpdateDir, 'subpackages'));
    }

    console.log('开始制作manifest文件');
    const packageUrl = `${configData.hotUpdateUrl}${configData.title}/${ver}/`;
    let manifest = {
        packageUrl: packageUrl,
        remoteManifestUrl: `${packageUrl} project.manifest`,
        remoteVersionUrl: `${packageUrl} version.manifest`,
        version: `${ver}`,
        assets: {},
        searchPaths: []
    };


    readDir(path.join(buildDir, 'src'), manifest.assets);
    readDir(path.join(buildDir, 'assets'), manifest.assets);
    readDir(path.join(buildDir, 'jsb-adapter'), manifest.assets);

    var destManifest = path.join(hotUpdateDir, 'project.manifest');
    var destVersion = path.join(hotUpdateDir, 'version.manifest');

    fs.writeFile(destManifest, JSON.stringify(manifest), (err) => {
        if (err) throw err;
        console.log('Manifest successfully generated');
    });

    delete manifest.assets;
    delete manifest.searchPaths;
    fs.writeFile(destVersion, JSON.stringify(manifest), (err) => {
        if (err) throw err;
        console.log('Version successfully generated');
    });

    try {
        return true;
    } catch (e) {
        console.error(e);
        return false;
    }
}

function readDir(dir, obj) {
    let stat = fs.statSync(dir);
    if (!stat.isDirectory()) {
        return;
    }
    let buildDir = path.join(configData.outputDir, configData.title, 'jsb-link');
    let subpaths = fs.readdirSync(dir), subpath, size, md5, compressed, relative;
    for (let i = 0; i < subpaths.length; ++i) {
        if (subpaths[i][0] === '.') {
            continue;
        }
        subpath = path.join(dir, subpaths[i]);
        stat = fs.statSync(subpath);
        if (stat.isDirectory()) {
            readDir(subpath, obj);
        }
        else if (stat.isFile()) {
            // Size in Bytes
            size = stat['size'];
            md5 = crypto.createHash('md5').update(fs.readFileSync(subpath)).digest('hex');
            compressed = path.extname(subpath).toLowerCase() === '.zip';

            relative = path.relative(buildDir, subpath);
            relative = relative.replace(/\\/g, '/');
            // relative = encodeURI(relative);
            obj[relative] = {
                'size': size,
                'md5': md5
            };
            if (compressed) {
                obj[relative].compressed = true;
            }
        }
    }
}

function packAPK() {
    // 重要配置
    let storeFile;
    let password;
    let alias;
    let keyPassword;
    // 这个比较重要，不要配置在配置里
    // 此处示例为CocosCreator的调试key
    if (os.platform() === 'darwin') {
        storeFile = `/Applications/CocosCreator/Creator/${configData.engineVer}/CocosCreator.app/Contents/Resources/static/build-templates/native/debug.keystore`;
        password = '123456';
        alias = 'debug_keystore';
        keyPassword = '123456';
    } else if (os.platform() === 'win32') {
        storeFile = `D:/CocosDashboard/resources/.editors/Creator/${configData.engineVer}/resources/static/build-templates/native/debug.keystore`;
        password = '123456';
        alias = 'debug_keystore';
        keyPassword = '123456';
    }

    const androidDir = path.join(configData.outputDir, configData.title, 'jsb-link', 'frameworks', 'runtime-src', 'proj.android-studio');
    
    // 制作icon
    const icons = [
        { url: 'mipmap-ldpi', width: 36, height: 36 },
        { url: 'mipmap-mdpi', width: 48, height: 48 },
        { url: 'mipmap-hdpi', width: 72, height: 72 },
        { url: 'mipmap-xhdpi', width: 96, height: 96 },
        { url: 'mipmap-xxhdpi', width: 144, height: 144 },
        { url: 'mipmap-xxxhdpi', width: 192, height: 192 }
    ];
    const width = [];
    for (let i = 0; i < icons.length; i++) {
        const iconPath = path.join(androidDir, 'res', icons[i].url);
        mkdirSync(iconPath);
        images(path.join(configData.projectDir, 'settings', 'buildConfig_Jsb', 'logo.png'))
            .size(icons[i].width)
            .save(path.join(iconPath, 'ic_launcher.png'));
    }

    // 修改settings.gradle 删除':game'和':instantapp' 项目
    // 不删除，则下面的打包语句需要对应修改成需要打包的项目
    const sgPath = path.join(androidDir, 'settings.gradle');
    let sgData = fs.readFileSync(sgPath).toString().replace(/\,[ ]*\'\:game\'[ ]*\,[ ]*\'\:instantapp\'/, "");

    fs.writeFileSync(sgPath, sgData);

    // gralde.properties的修改
    const gpPath = path.join(androidDir, 'gradle.properties');
    let gpData = fs.readFileSync(gpPath).toString();
    gpData = gpData.replace(/PROP_COMPILE_SDK_VERSION=.*/, `PROP_COMPILE_SDK_VERSION=${configData.apiLevel}`);
    gpData = gpData.replace(/PROP_TARGET_SDK_VERSION=.*/, `PROP_TARGET_SDK_VERSION=${configData.apiLevel}`);
    gpData = gpData.replace(/PROP_APP_ABI=.*/, `PROP_APP_ABI=${JSON.stringify(configData.appABIs).replace('[', '').replace(']', '').replace(/\"/g, '').replace(/\,/g, ":")}`);
    gpData = gpData.replace(/RELEASE_STORE_FILE=.*/, `RELEASE_STORE_FILE=${storeFile}`);
    gpData = gpData.replace(/RELEASE_STORE_PASSWORD=.*/, `RELEASE_STORE_PASSWORD=${password}`);
    gpData = gpData.replace(/RELEASE_KEY_ALIAS=.*/, `RELEASE_KEY_ALIAS=${alias}`);
    gpData = gpData.replace(/RELEASE_KEY_PASSWORD=.*/, `RELEASE_KEY_PASSWORD=${keyPassword}`);
    fs.writeFileSync(gpPath, gpData);

    // // 升级gradle的修改
    // // 1. 修改gradle-wrapper.properties
    // const gwpPath = path.join(androidDir, 'gradle', 'wrapper', 'gradle-wrapper.properties');
    // const gwpData = fs.readFileSync(gwpPath).toString().replace(/distributionUrl\=.*/, "distributionUrl=https\\://services.gradle.org/distributions/gradle-4.10.3-all.zip");
    // console.log('======>', gwpData)
    // fs.writeFileSync(gwpPath, gwpData);

    // // 2. 修改工程的build.gradle
    // const pbgPath = path.join(androidDir, 'build.gradle');
    // const pbgData = fs.readFileSync(pbgPath).toString().replace(/gradle\:[0-9.]+/, "gradle:3.6.4");
    // fs.writeFileSync(pbgPath, pbgData);

    // // 3. 修改mk
    // const mkPath = path.join(androidDir, 'jni', 'CocosAndroid.mk');
    // let mkText = fs.readFileSync(mkPath).toString().replace('cocos2djs_shared', 'cocos2djs');
    // fs.writeFileSync(mkPath, mkText);

    // // 升级gradle的修改完毕

    // const abgPath = path.join(androidDir, 'app', 'build.gradle');
    // let gradleData = fs.readFileSync(abgPath).toString();
    // let matcher = /\"\$\{outputDir\}\/[a-zA-Z-]+\"/g;
    // let matchs = gradleData.match(matcher);
    // if (null != matchs) {
    //     for (let i = 0; i < matchs.length; i++) {
    //         let newStr = `outputDir.dir("${matchs[i].substring(matchs[i].indexOf('/') + 1, matchs[i].length - 1)}")`;
    //         gradleData = gradleData.replace(/\"\$\{outputDir\}\/[a-zA-Z-]+\"/, newStr);
    //     }
    // }
    // let matcher1 = /applicationId[ ]+\"[a-zA-Z0-9_.]+\"/;
    // gradleData = gradleData.replace(matcher1, `applicationId "${configData.packageName}"`);
    // let matcher2 = /versionCode[ ]+[0-9]+/;
    // gradleData = gradleData.replace(matcher2, `versionCode ${configData.appCode}`);
    // let matcher3 = /versionName[ ]+\"[0-9.]+\"/;
    // gradleData = gradleData.replace(matcher3, `versionName "${configData.appVer}"`);

    // fs.writeFileSync(abgPath, gradleData);

    console.log('开始构建APK');
    try {
        if (os.platform() === 'darwin') {
            // const gradlewSpawn = spawn('gradlew', ['assembleRelease'], { shell: true, cwd: androidDir });
            // 实时结果查看
            // const gradlewSpawn = spawn('./gradlew', [':' + configData.title + ':assembleRelease'], { cwd: androidDir });
            // gradlewSpawn.stdout.on('data', function (chunk) {
            //     console.log(chunk.toString());
            // });
            // gradlewSpawn.stderr.on('data', (data) => {
            //     console.log(data);
            // });
            // gradlewSpawn.on('close', function (code) {
            //     console.log('close code : ' + code);
            // })
            // gradlewSpawn.on('exit', (code) => {
            //     console.log('exit code : ' + code);
            // });
        } else if (os.platform() === 'win32') {
            let paramStr = configData.isDebug ? 'assembleDebug' : 'assembleRelease'
            console.log('_____________________________________________________________________<<<', paramStr)
            const gradlewSpawn = spawn('gradlew', [paramStr], { shell: true, cwd: androidDir });
            gradlewSpawn.stdout.on('data', function (chunk) {
                console.log(chunk.toString());
            });
            gradlewSpawn.on('close', function (code) {
                console.log('close code : ' + code);
            })
            gradlewSpawn.on('exit', (code) => {
                console.log('exit code : ' + code);
            });
        }
        return true;
    } catch (e) {
        console.error(e);
        return false;
    }
}


function saveConfig() {
    const backPath = path.join(configData.projectDir, 'settings', 'buildConfig_Jsb', 'pack.back.json');
    fs.writeFileSync(backPath, JSON.stringify(backData));
    console.log('写入备份成功');
}

function compareVersion(ver1, ver2) {
    var arr1 = ver1.split('.');
    var arr2 = ver2.split('.');
    var length = Math.min(arr1.length, arr2.length);
    for (var i = 0; i < length; i++) {
        if (arr1[i] == arr2[i]) {
            continue;
        }
        return arr1[i] > arr2[i];
    }
    return arr1.length >= arr2.length;
}

function mkdirSync(dirPath, mode) {
    if (fs.existsSync(dirPath)) {
        return true;
    }
    if (mkdirSync(path.dirname(dirPath), mode)) {
        fs.mkdirSync(dirPath, mode);
        return true;
    }
}

function copyFileSync(src, dst) {
    if (!fs.existsSync(src)) {
        console.error('原路径不存在', src);
        return;
    }
    const stat = fs.statSync(src);
    if (stat.isFile()) {
        let dstDir = path.parse(dst).dir;
        if (!fs.existsSync(dstDir)) {
            mkdirSync(dstDir);
        }
        fs.copyFileSync(src, dst);
    } else if (stat.isDirectory()) {
        const files = fs.readdirSync(src);
        files.forEach((file) => {
            // console.log(src, file, dst);
            const srcPath = path.join(src, file);
            const dstPath = path.join(dst, file);
            copyFileSync(srcPath, dstPath);
        });
    }
}
